package com.bjian.CodeSandBox.codeSandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.bjian.CodeSandBox.codeSandbox.model.ExecuteCodeRequest;
import com.bjian.CodeSandBox.codeSandbox.model.ExecuteCodeResponse;
import com.bjian.CodeSandBox.codeSandbox.model.JudgeInfo;
import com.bjian.CodeSandBox.unitl.ProcessUtil;
import com.bjian.CodeSandBox.unitl.model.ExeuteMessage;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public abstract class CodeSandBoxTemplate implements CodeSandbox {

    private final static Long TIME_OUT = 5000L;
    private final static String userDIR;//读取用户路径
    private final static String globalCodePathName;

    static {
        userDIR = System.getProperty("user.dir");//读取用户路径
        globalCodePathName = userDIR + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "tmpCodeDir";
    }

    /**
     * 1.把用户代码保存为代码文件
     * @param code 用户做题代码
     * @return file
     */
    public File SaveFile(String code){
        //todo 把代码字符串保存为文件编译前 使用字典树（wordTree）校验一遍是否包含危险代码（暂未实现）
        if (!FileUtil.isFile(globalCodePathName)) {
            FileUtil.mkdir(globalCodePathName);
        }

        String userCodeParentPathName = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeParentPathName + File.separator + "Main.java";
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
        return userCodeFile;
    }

    /**
     * 2.编译代码，返回编译运行信息
     * @param userCodeFile 要编译的java文件
     * @return 编译运行信息
     */
    public ExeuteMessage compileCode(File userCodeFile){
        String compiledCmd = String.format("javac -encoding utf-8 %s", userCodeFile.getAbsolutePath());//组合最终要运行的编译命令
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(compiledCmd);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        ExeuteMessage exeuteMessage = ProcessUtil.runProcessAndGetMessage(process, "编译");
        return exeuteMessage;
    }

    /**
     * 3.执行代码，得到运行结果列表
     * @param userCodeFile 代码文件类
     * @param inputList 测试数据列表
     * @return 结果列表
     */
    public List<ExeuteMessage> runCodeFile(File userCodeFile,List<String> inputList){
        List<ExeuteMessage> exeuteMessageList = new ArrayList<>();

        for (String inputArgs : inputList) {// 循环执行，每组数据运行一次
            String runCmd = String.format("java -Dfire.encoding=UTF-8 -cp %s Main %s", userCodeFile.getParent(), inputArgs);
            Process runProcess = null;
            try {
                runProcess = Runtime.getRuntime().exec(runCmd);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            Process finalRunProcess = runProcess;
            new Thread(() -> {//运行计时线程，当超过设定最大执行时间则中断运行代码的线程（该实现较为简易，可使用Future和ExecutorService接口更为完整的控制）
                try {
                    Thread.sleep(TIME_OUT);
                    if (finalRunProcess.isAlive()) {
                        finalRunProcess.destroy();
                        System.out.println("超时了，中断");
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start();
            ExeuteMessage runExeuteMessage = ProcessUtil.runProcessAndGetMessage(runProcess, "运行");
            exeuteMessageList.add(runExeuteMessage);
        }
        return exeuteMessageList;
    }

    public ExecuteCodeResponse DisposalResults(List<ExeuteMessage> exeuteMessageList){
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        List<String> outputList = new ArrayList<>();
        long maxTime = Long.MIN_VALUE;
        for (ExeuteMessage RunExeuteMessage : exeuteMessageList) {
            System.out.println(RunExeuteMessage);
            String errorMessage = RunExeuteMessage.getErrorMessage();
            if (StrUtil.isNotBlank(errorMessage)) {
                executeCodeResponse.setMessage(errorMessage);
                // 用户提交的代码执行中存在错误
                executeCodeResponse.setStatus(3);
                break;
            }
            //统计运行时间，选择最大运行时间保存
            Long runTime = RunExeuteMessage.getRunTime();
            maxTime = Math.max(maxTime, runTime);
            String OutputResult = RunExeuteMessage.getMessage();
            outputList.add(OutputResult);
        }
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        executeCodeResponse.setJudgeInfo(judgeInfo);
        System.out.print(outputList);
        executeCodeResponse.setOutputList(outputList);
        //System.out.println(executeCodeResponse);
        return executeCodeResponse;
    }

    public boolean cleanRunFile(File userCodeFile){
        boolean del = true;
        if (userCodeFile.getParentFile() != null) {
            System.out.println(userCodeFile.getAbsolutePath());
//                boolean del = FileUtil.del(userCodeFile.getAbsolutePath());
//                System.out.println("删除" + (del ? "成功" : "失败"));
        }
        return del;
    }

    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        if (executeCodeRequest == null) {
            throw new RuntimeException();
        }
        String code = executeCodeRequest.getCode();
        List<String> inputList = executeCodeRequest.getInputList();

            //1.把用户代码保存为代码文件
            File userCodeFile = SaveFile(code);
//          System.out.println(userCodeFile.getAbsolutePath());

            //2.编译代码，得到class文件
            ExeuteMessage exeuteMessage = compileCode(userCodeFile);
            System.out.println(exeuteMessage);
            //3.执行代码，得到运行结果
            List<ExeuteMessage> exeuteMessageList = runCodeFile(userCodeFile, inputList);
            //4.收集整理运行结果
            ExecuteCodeResponse executeCodeResponse = DisposalResults(exeuteMessageList);
            //5.文件清理，释放空间
        boolean b = cleanRunFile(userCodeFile);
        return executeCodeResponse;
    }

    /**
     * 用于出现异常时，能够正常返回数据。
     * 包装一个用于出现异常时返回的特殊对象
     * @param throwable 异常信息
     * @return ExecuteCodeResponse 包含异常信息的沙箱运行结果对象
     */
    private ExecuteCodeResponse getErrorExecuteCodeResponse(Throwable throwable) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutputList(new ArrayList<>());
        executeCodeResponse.setMessage(throwable.getMessage());
        executeCodeResponse.setStatus(2);//表示沙箱代码错误
        executeCodeResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeResponse;
    }

}
